/**
 * 
 */
package sg.edu.nus;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author Christian
 *
 */
public class TestRunner {
	
	private boolean logDetails = false;
	private boolean unitTest = false;
	
	private class IntermediateResult {
		int run;
		long duration;
		int foundRoles;
		double accuracyx1;
		double accuracyx2;
		double accuracyx3;
	}
	
	public TestRunner(String[] args) {
		// get initial role priority factor
		int initRolePriority = Integer.parseInt(args[6]);
		// get test runs
		int testRuns = Integer.parseInt(args[7]);
		List<IntermediateResult> results = new ArrayList<IntermediateResult>();
		// check if unit test
		unitTest = Boolean.valueOf(args[8]);
		// check log level
		logDetails = Boolean.valueOf(args[9]);

		Map<Integer, Integer[]> genRoles = null;
		
		System.out.println("Starting " + testRuns + " role miner runs...");
		
		for(int run=0; run < testRuns; run++) {
			System.out.println("\nRole miner run: " + run);
			System.out.println("-----------------");
			// create test data
			TestDataGenerator testDataGenerator = null;
			if(!unitTest) {
				System.out.println("Using random test data");
				testDataGenerator = new RandomTestDataGenerator(Integer.parseInt(args[1]), Integer.parseInt(args[2]), 
						Integer.parseInt(args[3]), Integer.parseInt(args[4]), Integer.parseInt(args[5]));
			}
			else {
				System.out.println("Using unit test data");
				testDataGenerator = new UnitTestDataGenerator();
			}
			// log test data
			if(logDetails) testDataGenerator.dumpData();
			// get start time
			long startTime = System.currentTimeMillis();
			// get role miner
			RoleMiner roleMiner = null;
			if(Boolean.valueOf(args[0])) {
				System.out.println("\nUsing complete miner");
				roleMiner = new CompleteMiner(testDataGenerator.getUsersWithPermissions(), initRolePriority);
			}
			else {
				System.out.println("\nUsing fast miner");
				roleMiner = new FastMiner(testDataGenerator.getUsersWithPermissions(), initRolePriority);
			}

			genRoles = roleMiner.getGenRoles();
			long duration = System.currentTimeMillis() - startTime;
			// save intermediate results
			IntermediateResult intermedRes = new IntermediateResult();
			intermedRes.run = run;
			intermedRes.duration = duration;
			intermedRes.foundRoles = genRoles.size();
			intermedRes.accuracyx1 = getAccuracy(roleMiner.getCountSortedResult(), testDataGenerator.getOriginalRoles(), 1);
			intermedRes.accuracyx2 = getAccuracy(roleMiner.getCountSortedResult(), testDataGenerator.getOriginalRoles(), 2);
			intermedRes.accuracyx3 = getAccuracy(roleMiner.getCountSortedResult(), testDataGenerator.getOriginalRoles(), 3);
			results.add(intermedRes);
			
			System.out.println("Role miner run=" + run + " finished:");
			if(logDetails) {
				roleMiner.dumpData();
			}
		}
		System.out.println("\nFinal results:");
		System.out.println("==============");
		// print results
		printResults(results, testRuns);
	}
	
	private void printResults(List<IntermediateResult> results, int testRuns) {
		IntermediateResult sum = new IntermediateResult();
		
		System.out.println("run, duration(ms), foundRoles, accuracy x 1, accuracy x 2, accuracy x 3");
		
		for(IntermediateResult intermedRes : results) {
			System.out.println(intermedRes.run + "," + intermedRes.duration + "," + intermedRes.foundRoles + "," +
					intermedRes.accuracyx1 + "," + intermedRes.accuracyx2 + "," + intermedRes.accuracyx3);
			sum.duration += intermedRes.duration;
			sum.foundRoles += intermedRes.foundRoles;
			sum.accuracyx1 += intermedRes.accuracyx1;
			sum.accuracyx2 += intermedRes.accuracyx2;
			sum.accuracyx3 += intermedRes.accuracyx3;
		}
		System.out.println("\n ," + sum.duration / testRuns + "," + (sum.foundRoles/testRuns) + "," +
				(sum.accuracyx1/testRuns) + "," + (sum.accuracyx2/testRuns) + "," + (sum.accuracyx3/testRuns));
	}
	
	private double getAccuracy(TreeMap<Integer, List<Integer>> sortedResult, Map<Integer, Integer[]> originalRoles, int factor) {
		int i = 0;
		int match = 0;
		int originalRolesCount = originalRoles.size();
		List<Integer> roleKeyList = null;
		for(int count : sortedResult.descendingKeySet()) {
			roleKeyList = sortedResult.get(count);
			for(Integer roleKey : roleKeyList) {
				if(i < originalRolesCount * factor) {
					if(originalRoles.containsKey(roleKey)) match++;
				}
				else {
					break;
				}
				i++;
			}
		}
		
		return (double)(match * 100/originalRolesCount);
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// check arguments
		if(args.length < 7) {
			System.out.println("Usage: completeMiner(true|false) #roleCount #userCount #permissionCount #maxRolesPerUser " +
					"#maxPermissionsPerRole initRolePriority #testRuns unitTestData(true|false) logDetails(true|false)");
			System.exit(1);
		}
		// create testrunner
		new TestRunner(args);

	}

}
